package hotciv.view;

import hotciv.framework.*;

import java.awt.*;
import java.util.*;

import minidraw.framework.*;
import minidraw.standard.*;

/** CivDrawing is a specialized Drawing (model component) from
 * MiniDraw that dynamically builds the list of Figures for MiniDraw
 * to render the Unit and other information objects that are visible
 * in the Game instance.
 *
 * This is a TEMPLATE for the dSoftArk Exercise! This means
 * that it is INCOMPLETE and that there are several options
 * for CLEANING UP THE CODE when you add features to it!

   This source code is from the book 
     "Flexible, Reliable Software:
       Using Patterns and Agile Development"
     published 2010 by CRC Press.
   Author: 
     Henrik B Christensen 
     Computer Science Department
     Aarhus University

   This source code is provided WITHOUT ANY WARRANTY either 
   expressed or implied. You may study, use, modify, and 
   distribute it for non-commercial purposes. For any 
   commercial use, see http://www.baerbak.com/

 */

public class CivDrawing extends StandardDrawing
implements Drawing, GameObserver {

	/** the Game instance that this UnitDrawing is going to render units
	 * from */
	protected Game game;
	
	private TextFigure ageText;
	private TextFigure moveCountText;
	private ImageFigure workForceFocusIcon;
	private UnitFigure cityProductionIcon;
	
	public CivDrawing(DrawingEditor editor, Game game ) {
		super();
		this.game = game;

		// register this unit drawing as listener to any game state
		// changes...
		game.addObserver(this);
		// ... and build up the set of cities associated with
		// cities in the game.
		defineCityMap();
		// ... and build up the set of figures associated with
		// units in the game.
		defineUnitMap();
		// and the set of 'icons' in the status panel
		defineIcons();
		
		writeAge(game.getAge());
	}

	/** The UnitDrawing should not allow client side
	 * units to add and manipulate figures; only figures
	 * that renders game objects are relevant, and these
	 * should be handled by observer events from the game
	 * instance. Thus this method is 'killed'.
	 */
	public Figure add(Figure arg0) {
		throw new RuntimeException("Should not be used...");
	}

	/** store all moveable figures visible in this drawing = units and cities */
	protected Map<Unit,UnitFigure> figureMap = null;
	protected Map<City,CityFigure> cityMap = null;

	/** erase the old list of units, and build a completely new
	 * one from scratch by iterating over the game world and add
	 * Figure instances for each unit in the world.
	 */
	private void defineUnitMap() {
		// ensure no units of the old list are accidental in
		// the selection!
		clearSelection();

		figureMap = new HashMap<Unit,UnitFigure>();
		Position p;
		for ( int r = 0; r < GameConstants.WORLDSIZE; r++ ) {
			for ( int c = 0; c < GameConstants.WORLDSIZE; c++ ) {
				p = new Position(r,c);
				Unit unit = game.getUnitAt(p);
				if ( unit != null ) {
					String type = unit.getTypeString();
					// convert the unit's Position to (x,y) coordinates
					Point point = new Point( GfxConstants.getXFromColumn(p.getColumn()),
							GfxConstants.getYFromRow(p.getRow()) );
					UnitFigure unitFigure =
							new UnitFigure( type, point, unit );
					unitFigure.addFigureChangeListener(this);
					figureMap.put(unit, unitFigure);

					// also insert in superclass list as it is
					// this list that is iterated by the
					// graphics rendering algorithms
					super.add(unitFigure);
				}
			}
		}
	}
	
	/** erase the old list of cities, and build a completely new
	 * one from scratch by iterating over the game world and add
	 * Figure instances for each city in the world.
	 */
	private void defineCityMap() {
		// ensure no cities of the old list are accidental in
		// the selection!
		clearSelection();

		cityMap = new HashMap<City,CityFigure>();
		Position p;
		for ( int r = 0; r < GameConstants.WORLDSIZE; r++ ) {
			for ( int c = 0; c < GameConstants.WORLDSIZE; c++ ) {
				p = new Position(r,c);
				City city = game.getCityAt(p);
				if ( city != null ) {
					// convert the city's Position to (x,y) coordinates
					Point point = new Point( GfxConstants.getXFromColumn(p.getColumn()),
							GfxConstants.getYFromRow(p.getRow()) );
					CityFigure cityFigure =
							new CityFigure(city, point);
					cityFigure.addFigureChangeListener(this);
					cityMap.put(city, cityFigure);

					// also insert in superclass list as it is
					// this list that is iterated by the
					// graphics rendering algorithms
					super.add(cityFigure);
				}
			}
		}
	}

	private ImageFigure turnShieldIcon;
	private void defineIcons() {
		// very much a template implementation :)
		turnShieldIcon = 
				new ImageFigure( "redshield",
						new Point( GfxConstants.TURN_SHIELD_X,
								GfxConstants.TURN_SHIELD_Y ) ); 
		// insert in superclass figure list to ensure graphical
		// rendering.
		super.add(turnShieldIcon);
	}
	
	private void writeAge(int age) {
		String ageString;
		if (age < 0) {
			ageString = Math.abs(age) + " BC";
		}
		else {
			ageString = age + " AC";
		}
		if (ageText == null) {
			ageText = new TextFigure(ageString, 
				new Point(GfxConstants.AGE_TEXT_X,
						GfxConstants.AGE_TEXT_Y) );
			super.add(ageText);
		}
		ageText.setText(ageString);
	}
	
	private void inspectCity(Position p) {
		// Remove icons
		if (cityProductionIcon != null) {
			super.remove(cityProductionIcon);
			cityProductionIcon = null;
		}
		if (workForceFocusIcon != null) {
			super.remove(workForceFocusIcon);
			workForceFocusIcon = null;
		}
		
		// If there is no city at p, stop the execution
		final City c = game.getCityAt(p);
		if (c == null) {
			return ;
		}
		
		// Add a unit production icon
		String production = c.getProduction();
		cityProductionIcon = new UnitFigure(production, 
				new Point(GfxConstants.CITY_PRODUCTION_X,
						GfxConstants.CITY_PRODUCTION_Y), new Unit() {

							@Override
							public String getTypeString() {
								return GameConstants.ARCHER;
							}

							@Override
							public Player getOwner() {
								return c.getOwner();
							}

							@Override
							public int getMoveCount() {
								return 0;
							}

							@Override
							public int getDefensiveStrength() {
								return 0;
							}

							@Override
							public int getAttackingStrength() {
								return 0;
							}
			
		});
		super.add(cityProductionIcon);
		
		// Add work focus focus icon
		String focus = c.getWorkforceFocus();
		workForceFocusIcon = new ImageFigure(focus, 
				new Point(GfxConstants.WORKFORCEFOCUS_X,
						GfxConstants.WORKFORCEFOCUS_Y) );
		super.add(workForceFocusIcon);
	}
	
	private void inspectUnit(Position p) {
		
		// Remove text
		if (moveCountText != null) {
			super.remove(moveCountText);
			moveCountText = null;
		}
		
		// If there is no unit at p, stop the execution
		final Unit u = game.getUnitAt(p);
		if (u == null) {
			return ;
		}
		
		// Add move count text
		String movesLeft = "" + u.getMoveCount();
		moveCountText = new TextFigure(movesLeft, new Point(GfxConstants.UNIT_COUNT_X, GfxConstants.UNIT_COUNT_Y));
		super.add(moveCountText);
	}

	// === Observer Methods ===

	public void worldChangedAt(Position pos) {
		clearSelection();
		// this is a really brute-force algorithm: destroy
		// all known units and build up the entire set again
		for ( Figure f : figureMap.values() ) {
			super.remove(f);
		}
		defineCityMap();
		defineUnitMap();
	}

	public void turnEnds(Player nextPlayer, int age) {
		String playername = "red";
		if ( nextPlayer == Player.BLUE ) { playername = "blue"; }
		turnShieldIcon.set( playername+"shield",
				new Point( GfxConstants.TURN_SHIELD_X,
						GfxConstants.TURN_SHIELD_Y ) );
		writeAge(game.getAge());
	}

	public void tileFocusChangedAt(Position position) {
		inspectCity(position);
		inspectUnit(position);
	}
}
